package genetic.q2;

import java.util.Collections;
import java.util.List;
import java.util.Vector;


public class TSPPhenotype extends PuzzlePhenotype{

	int size;
	TSPGraph graph;

	public TSPPhenotype(int size, TSPGraph g){
		this.size = size;
		this.graph = g;
	}

	@Override
	public List<PuzzleGenotype> getInitialPopulation(int populationSize) {
		return getInitialRandomPopulation(populationSize);
	}

	@Override
	public PuzzleGenotype createRandomInstance() {
		return new TSPGenotype(getRandomIntString(size,0), graph);
	}

	@Override
	public PuzzleGenotype[] crossover(PuzzleGenotype g1, PuzzleGenotype g2) {
		int[][] kids = pmxCrossover(g1, g2);
		return new PuzzleGenotype[]{new TSPGenotype(kids[0], graph), new TSPGenotype(kids[1], graph)};
	}

	@Override
	public double fitness(PuzzleGenotype g) {
		int[] rep = g.getIntString();
		int length = graph.getDistance(rep[rep.length-1]-1, rep[0]-1);
		for (int i=0; i<rep.length-1; i++){
			length = length + graph.getDistance(rep[i]-1, rep[i+1]-1);
		}
		//	return graph.maxArc / (double)length * 100.0;
		return  (double)length;
	}

	@Override
	public List<PuzzleGenotype> getMatingPool(List<PuzzleGenotype> population, int populationSize) {
		return getMatingPoolTournamentSelection(population, populationSize, 2, 1, 1);
	}

	@Override
	public PuzzleGenotype mutate(PuzzleGenotype g, double Pm) {
		if (Math.random()<Pm){
//			return new TSPGenotype(customMutation(g.getIntString()), graph);
			//			return new TSPGenotype(swapMutation(g.getIntString()), graph);
						return new TSPGenotype(inverseMutation(g), graph);
			//			return new TSPGenotype(scrambleMutation(g.getIntString()), graph);
		} else {
			return g;
		}
	}

	protected int[] customMutation(int[] org){
		int[] ans = new int[org.length];
		int city1 = (int)(Math.random()*org.length);
		//		int numCities = (int)(org.length/6.0);
		int numCities = 5;

		Vector<Integer> minCities = new Vector<Integer>();
		int index = 0;
		while(minCities.size()<numCities){
			if (Math.abs(index-city1)>1){
				minCities.add(index);
			}
			index++;
		}
		for (int i=0; i<org.length; i++) if (Math.abs(i-city1)>1){			//find the minimum 6 cities
			//assumes distances are the same for both ways

			//insert each of the elements to the vector and removes the maximal one
			//that way the vector always has 6 cities
			minCities.add(i);
			int max = 0;
			int minCity = 0;
			for (int j=0; j<minCities.size(); j++){
				if (max < graph.getDistance(org[minCities.get(j)]-1, org[city1]-1)){
					max = graph.getDistance(org[minCities.get(j)]-1, org[city1]-1);
					minCity = j;
				}
			}
			minCities.remove(minCity);
		}
		Collections.shuffle(minCities);
		int city2 = minCities.remove(0);

		for (int i=0; i<=Math.min(city1, city2-1); i++){
			ans[i] = org[i];
		}
		int num;
		if (city1<city2){
			num = 1;
		} else {
			num =-1;
		}
		ans[city1+num] = org[city2];

		for (int i=Math.min(city1+2, city2); i<=Math.max(city1-2,city2); i++){
			ans[i] = org[i-num];
		}
		for (int i=Math.max(city1,city2+1); i<org.length; i++){
			ans[i] = org[i];
		}
		//		System.out.println(city1 +","+ city2 +","+ Arrays.toString(ans));
		return ans;

	}

	@Override
	public void updateFitness(List<PuzzleGenotype> population) {
	}

}
